package ink.xiaobaibai.provider;

import ink.xiaobaibai.common.Constants;
import ink.xiaobaibai.token.MyUsernamePasswordAuthenticationToken;
import ink.xiaobaibai.userDetails.UserNumberDetailsServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @description: 用户密码登录验证器
 * @author: 小白白
 * @create: 2021-05-12
 **/

public class MyUsernamePasswordAuthenticationProvider implements AuthenticationProvider {

    private UserDetailsService userDetailsService;
    private StringRedisTemplate stringRedisTemplate;
    private PasswordEncoder passwordEncoder;

    public MyUsernamePasswordAuthenticationProvider(UserDetailsService userDetailsService, StringRedisTemplate stringRedisTemplate, PasswordEncoder passwordEncoder) {
        this.userDetailsService = userDetailsService;
        this.stringRedisTemplate = stringRedisTemplate;
        this.passwordEncoder = passwordEncoder;
    }

    /**
     * 注意此过滤器无需经过rbac框架,所以抛出异常。
     *
     * @param authentication
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        MyUsernamePasswordAuthenticationToken myUsernamePasswordAuthenticationToken = (MyUsernamePasswordAuthenticationToken) authentication;

        String username = myUsernamePasswordAuthenticationToken.getUsername();
        String password = myUsernamePasswordAuthenticationToken.getPassword();
        String uuid = myUsernamePasswordAuthenticationToken.getUuid();
        String verificationCode = myUsernamePasswordAuthenticationToken.getVerificationCode();

        //先校验验证码
        if (!this.validVerificationCode(uuid, verificationCode)) {
            throw new BadCredentialsException("验证码错误");
        }

        //再校验密码
        UserNumberDetailsServiceImpl.MyUser userDetails = (UserNumberDetailsServiceImpl.MyUser) this.userDetailsService.loadUserByUsername(username);

        if (userDetails == null || !this.passwordEncoder.matches(password, userDetails.getPassword())) {
            throw new BadCredentialsException("用户名或密码错误");
        }

        //通过校验
        MyUsernamePasswordAuthenticationToken thisOkAuthenticationToken = myUsernamePasswordAuthenticationToken.getThisOkAuthenticationToken(userDetails.getAuthorities(), userDetails.getUserId());
        thisOkAuthenticationToken.setDetails(myUsernamePasswordAuthenticationToken.getDetails());
        //已认证
        thisOkAuthenticationToken.setAuthenticated(true);

        return thisOkAuthenticationToken;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return MyUsernamePasswordAuthenticationToken.class.isAssignableFrom(aClass);
    }

    private boolean validVerificationCode(String uuid, String verificationCode) {
        if (StringUtils.isBlank(verificationCode)) {
            return false;
        }
        String code = this.stringRedisTemplate.opsForValue().get(Constants.REDIS_KEY_VERIFICATION_CODE + uuid);
        //用完清除
        this.stringRedisTemplate.delete(Constants.REDIS_KEY_VERIFICATION_CODE + uuid);
        if (StringUtils.isBlank(code)) {
            return false;
        }
        return StringUtils.equalsIgnoreCase(verificationCode, code);
    }

}
